﻿using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Web.Http;
using Tyche.Common.Filter;
using Tyche.Common.ProxyBuilder;
using Tyche.Common.Resource;

namespace Tyche.WebApiService
{
    public class TycheWebApiServiceBuilder : ITycheProxyBuilder
    {
        /// <summary>
        /// 构建代理
        /// </summary>
        /// <param name="moduleBuilder"></param>
        /// <param name="type"></param>
        public void Build(ModuleBuilder moduleBuilder, Type type)
        {
            // 构建代理类
            // 要求原实现类中只有一个构造函数且构造函数的参数已注册为TycheResource
            // 构建代理类之前先构建好构造函数中的参数
            // 代理继承ApiController
            var resource = TycheResourceManager.GetResource(type);

            if (resource.Proxy != null)
            {
                return;
            }

            if (resource.Implement != null)
            {
                foreach (var parameter in resource.Implement.GetConstructors().First().GetParameters())
                {
                    resource.Builder.Build(moduleBuilder, parameter.ParameterType);
                }
            }
            else
            {
                resource.Builder.Build(moduleBuilder, resource.Interface);
            }

            var typeBuilder = moduleBuilder.DefineType(resource.Implement.Name
                , TypeAttributes.Class | TypeAttributes.Public
                , typeof(ApiController)
                , new Type[] { resource.Interface });

            // 开始构建代理类的方法
            // 要求方法必须具有返回值(如无返回值是没有意义的)
            // 思路：
            // 1、代理方法调用原实现类中对应的方法
            // 2、执行原实现类方法前后会调用各个Filter
            // 3、利用EMIT构建代理方法体
            foreach (var method in resource.Interface.GetMethods().Where(m => m.ReturnType != typeof(void)))
            {
                // 参数类型
                var parameterTypes = method.GetParameters().Select(p => { return p.ParameterType; }).ToArray();

                // 原实现类的方法
                var implMethod = resource.Implement.GetMethod(method.Name, parameterTypes);

                // 原实现类上的过滤器

                var filters = implMethod
                    .GetCustomAttributes()
                    .Where(a => a.GetType().GetInterfaces().Any(i => i == typeof(ITycheFilter)))
                    .Select(a => { return (ITycheFilter)a; })
                    .ToArray();

                var methodBuilder = typeBuilder.DefineMethod(method.Name
                    , MethodAttributes.Public
                    | MethodAttributes.HideBySig
                    | MethodAttributes.NewSlot
                    | MethodAttributes.Virtual
                    | MethodAttributes.Final
                    , method.ReturnType
                    , parameterTypes);

                var isHttpMethodGet = method.GetCustomAttributes(typeof(HttpGetAttribute)).Any();

                // 添加HttpGet或HttpPost标签
                methodBuilder.SetCustomAttribute(
                    new CustomAttributeBuilder((isHttpMethodGet ? typeof(HttpGetAttribute) : typeof(HttpPostAttribute))
                    .GetConstructor(Type.EmptyTypes), new object[] { }));

                // 为方法每个参数设置名字
                var parameterNames = method.GetParameters().Select(p => { return p.Name; }).ToArray();

                for (var i = 0; i < parameterNames.Length; ++i)
                {
                    var parameterBuilder = methodBuilder.DefineParameter(i + 1, ParameterAttributes.None, parameterNames[i]);

                    // 如果是HttpGet则需要为参数添加FromUriAttribute标签
                    if (isHttpMethodGet)
                    {
                        parameterBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(FromUriAttribute)
                            .GetConstructor(Type.EmptyTypes), new object[] { }));
                    }
                }

                var generator = methodBuilder.GetILGenerator();

                // 返回结果
                var result = generator.DeclareLocal(method.ReturnType);

                // 异常
                var exception = generator.DeclareLocal(typeof(Exception));

                // 方法执行标签
                var normalLabel = generator.DefineLabel();

                // 返回标签
                var returnLabel = generator.DefineLabel();

                // 方法参数类型
                var argTypes = generator.DeclareLocal(typeof(Type[]));

                generator.Emit(OpCodes.Ldc_I4, parameterTypes.Length);
                generator.Emit(OpCodes.Newarr, typeof(Type));
                generator.Emit(OpCodes.Stloc_S, argTypes);

                for (var i = 0; i < parameterTypes.Length; ++i)
                {
                    generator.Emit(OpCodes.Ldloc_S, argTypes);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Ldtoken, parameterTypes[i]);
                    generator.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public));
                    generator.Emit(OpCodes.Stelem_Ref);
                }

                // Filters
                var tycheFilters = generator.DeclareLocal(typeof(ITycheFilter[]));

                generator.Emit(OpCodes.Ldc_I4, filters.Length);
                generator.Emit(OpCodes.Newarr, typeof(ITycheFilter));
                generator.Emit(OpCodes.Stloc_S, tycheFilters);

                // 获取Filters
                for (var i = 0; i < filters.Length; ++i)
                {
                    generator.Emit(OpCodes.Ldloc_S, tycheFilters);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Ldtoken, implMethod.DeclaringType);
                    generator.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public));
                    generator.Emit(OpCodes.Ldstr, method.Name);
                    generator.Emit(OpCodes.Ldloc_S, argTypes);
                    generator.Emit(OpCodes.Callvirt, typeof(Type).GetMethod("GetMethod", new Type[] { typeof(string), typeof(Type[]) }));
                    generator.Emit(OpCodes.Call, typeof(TycheFilterGetter).GetMethod("GetFilters", BindingFlags.Static | BindingFlags.Public));
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Ldelem_Ref);
                    generator.Emit(OpCodes.Castclass, typeof(ITycheFilter));
                    generator.Emit(OpCodes.Stelem_Ref);
                }

                // 参数数组
                var parameters = generator.DeclareLocal(typeof(object[]));

                generator.Emit(OpCodes.Ldc_I4, parameterTypes.Length);
                generator.Emit(OpCodes.Newarr, typeof(object));
                generator.Emit(OpCodes.Stloc_S, parameters);

                for (var i = 0; i < parameterTypes.Length; ++i)
                {
                    generator.Emit(OpCodes.Ldloc_S, parameters);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Ldarg_S, i + 1);

                    if (parameterTypes[i].IsPrimitive)
                    {
                        generator.Emit(OpCodes.Box, parameterTypes[i]);
                    }

                    generator.Emit(OpCodes.Stelem_Ref);
                }

                // 方法执行前
                for (var i = 0; i < filters.Length; ++i)
                {
                    generator.Emit(OpCodes.Ldloc_S, tycheFilters);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Ldelem_Ref);
                    generator.Emit(OpCodes.Ldstr, string.Format("{0}.{1}", method.DeclaringType.FullName, method.Name));
                    generator.Emit(OpCodes.Ldloc_S, parameters);
                    generator.Emit(OpCodes.Callvirt, typeof(ITycheFilter).GetMethod("Executing", new Type[] { typeof(string), typeof(object[]) }));
                }

                // 方法中断
                for (var i = 0; i < filters.Length; ++i)
                {
                    generator.Emit(OpCodes.Ldloc_S, tycheFilters);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Ldelem_Ref);
                    generator.Emit(OpCodes.Ldtoken, implMethod.ReturnType);
                    generator.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public));
                    generator.Emit(OpCodes.Callvirt, typeof(ITycheFilter).GetMethod("Interrupt", new Type[] { typeof(Type) }));
                    generator.Emit(OpCodes.Stloc_S, result);
                    generator.Emit(OpCodes.Ldloc_S, result);
                    generator.Emit(OpCodes.Ldnull);
                    generator.Emit(OpCodes.Ceq);
                    generator.Emit(OpCodes.Brfalse_S, returnLabel);
                }

                // 执行方法
                generator.MarkLabel(normalLabel);

                generator.BeginExceptionBlock();

                generator.Emit(OpCodes.Ldtoken, method.DeclaringType);

                generator.Emit(OpCodes.Call
                    , typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Public | BindingFlags.Static));
                generator.Emit(OpCodes.Ldc_I4_0);
                generator.Emit(OpCodes.Call
                    , typeof(TycheResourceManager).GetMethod("CreateInstance", new Type[] { typeof(Type), typeof(bool) }));

                for (var i = 0; i < parameterTypes.Length; ++i)
                {
                    generator.Emit(OpCodes.Ldarg_S, i + 1);
                }

                generator.Emit(OpCodes.Callvirt, method);
                generator.Emit(OpCodes.Stloc_S, result);

                generator.BeginCatchBlock(typeof(Exception));
                generator.Emit(OpCodes.Stloc_S, exception);

                // 方法异常处理
                for (var i = 0; i < filters.Length; ++i)
                {
                    generator.Emit(OpCodes.Ldloc_S, tycheFilters);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Ldelem_Ref);
                    generator.Emit(OpCodes.Ldloc_S, exception);
                    generator.Emit(OpCodes.Callvirt, typeof(ITycheFilter).GetMethod("Exception", new Type[] { typeof(Exception) }));
                }

                generator.Emit(OpCodes.Ldloc_S, exception);
                generator.Emit(OpCodes.Throw);

                generator.EndExceptionBlock();

                generator.MarkLabel(returnLabel);

                // 方法执行完成
                for (var i = 0; i < filters.Length; ++i)
                {
                    generator.Emit(OpCodes.Ldloc_S, tycheFilters);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Ldelem_Ref);
                    generator.Emit(OpCodes.Ldloc_S, result);
                    generator.Emit(OpCodes.Callvirt, typeof(ITycheFilter).GetMethod("Executed", new Type[] { typeof(object) }));
                }

                generator.Emit(OpCodes.Ldloc_S, result);
                generator.Emit(OpCodes.Ret);
            }

            resource.Proxy = typeBuilder.CreateType();
        }

        /// <summary>
        /// 绑定Service接口与Service实现类
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <param name="name"></param>
        public void Bind<T1, T2>(string name)
        {
            TycheResourceManager.SetResource(name, typeof(T1), typeof(T2), null, this);
        }
    }
}
